<template>
  <div class="chat-container">
    <div class="chat-header">
      <h1>DeepSeek问答系统</h1>
      <div class="rag-status" v-if="ragStatus">
        <span v-if="ragStatus.initialized">RAG系统已启用 (文档数: {{ ragStatus.document_count }})</span>
        <span v-else>RAG系统未初始化</span>
      </div>
    </div>
    
    <div class="chat-messages" ref="messagesContainer">
      <div v-for="(message, index) in messages" :key="index" :class="['message', message.role]">
        <div class="message-avatar">{{ message.role === 'user' ? '我' : 'AI' }}</div>
        <div class="message-content">
          <div v-if="message.context_used" class="context-indicator">
            <span>📚 已使用RAG上下文</span>
          </div>
          <div v-html="formatMessage(message.content)"></div>
        </div>
      </div>
      
      <div v-if="isLoading" class="message assistant loading">
        <div class="message-avatar">AI</div>
        <div class="message-content">
          <div class="typing-indicator">
            <span></span>
            <span></span>
            <span></span>
          </div>
        </div>
      </div>
    </div>
    
    <div class="rag-controls">
      <div class="upload-section">
        <input 
          type="file" 
          ref="fileInput" 
          @change="handleFileUpload" 
          accept=".txt" 
          style="display: none;"
        >
        <button @click="$refs.fileInput.click()" :disabled="isLoading">
          📁 上传文档
        </button>
        <span v-if="uploadingFile">上传中...</span>
        <span v-if="uploadStatus" :class="uploadStatus.success ? 'success' : 'error'">
          {{ uploadStatus.message }}
        </span>
      </div>
      <button @click="addSampleDocuments" :disabled="isLoading">
        📚 添加示例文档
      </button>
    </div>
    
    <div class="chat-input">
      <textarea 
        v-model="inputMessage" 
        @keyup.enter="sendMessage"
        placeholder="输入消息，按Enter发送..."
        :disabled="isLoading"
        rows="1"
      ></textarea>
      <button @click="sendMessage" :disabled="!inputMessage.trim() || isLoading">
        发送
      </button>
    </div>
  </div>
</template>

<script>
import axios from 'axios';

export default {
  name: 'App',
  data() {
    return {
      messages: [],
      inputMessage: '',
      isLoading: false,
      ragStatus: null,
      uploadStatus: null,
      uploadingFile: false
    };
  },
  
  mounted() {
    // 组件挂载后获取RAG状态
    this.getRagStatus();
  },
  
  methods: {
    async getRagStatus() {
      try {
        const response = await axios.get('/api/rag/status');
        if (response.data.success) {
          this.ragStatus = response.data.status;
        }
      } catch (error) {
        console.error('获取RAG状态失败:', error);
      }
    },
    
    async sendMessage() {
      if (!this.inputMessage || this.isLoading) return;
      
      // 添加用户消息到列表
      this.messages.push({
        role: 'user',
        content: this.inputMessage
      });
      
      // 清空输入框
      this.inputMessage = '';
      
      this.isLoading = true;
      
      // 滚动到底部
      this.scrollToBottom();
      
      try {
        // 创建AI消息对象，将在流式响应中逐步更新
        const aiMessageId = this.messages.length;
        this.messages.push({
          role: 'assistant',
          content: '',
          context_used: false,
          isStreaming: true
        });
        
        // 使用fetch API进行流式请求
        const response = await fetch('/api/chat', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({
            messages: this.messages.slice(0, -1), // 不包含刚刚添加的空AI消息
            stream: true
          })
        });
        
        if (!response.ok) {
          throw new Error(`HTTP error! Status: ${response.status}`);
        }
        
        // 获取响应体的读取器
        const reader = response.body.getReader();
        const decoder = new TextDecoder();
        let fullAnswer = '';
        let buffer = ''; // 用于累积不完整的SSE消息
        
        // 处理流式响应
        while (true) {
          const { done, value } = await reader.read();
          
          if (done) {
            break;
          }
          
          // 解码接收到的数据
          buffer += decoder.decode(value, { stream: true });
          
          // SSE格式是"data: {json}\n"，需要正确解析
          const lines = buffer.split('\n');
          buffer = ''; // 重置缓冲区
          
          for (let i = 0; i < lines.length; i++) {
            const line = lines[i].trim();
            
            // 忽略空行
            if (!line) continue;
            
            // 检查是否是data行
            if (line.startsWith('data:')) {
              try {
                // 提取JSON部分并解析
                const jsonStr = line.substring(5).trim();
                const data = JSON.parse(jsonStr);
                
                if (!data.error) {
                  // 处理正常响应
                  if (data.content) {
                    fullAnswer += data.content;
                    // 更新AI消息
                    this.messages[aiMessageId].content = fullAnswer;
                    this.$forceUpdate(); // 强制更新视图
                    this.scrollToBottom(); // 滚动到底部
                  }
                  
                  if (data.done) {
                    // 完成响应，设置上下文使用信息
                    this.messages[aiMessageId].context_used = data.context_used || false;
                    this.messages[aiMessageId].isStreaming = false;
                  }
                } else {
                  // 处理错误
                  throw new Error(data.error);
                }
              } catch (e) {
                // 保存未完成的行到缓冲区，或者如果是最后一行，则抛出错误
                if (i === lines.length - 1 && !done) {
                  buffer = line;
                } else {
                  console.error('解析SSE消息失败:', e);
                }
              }
            }
          }
        }
        
        if (!fullAnswer) {
          throw new Error('未收到任何响应内容');
        }
        
      } catch (error) {
        console.error('Error:', error);
        // 查找并更新最后一条AI消息为错误消息
        const lastMessage = this.messages[this.messages.length - 1];
        if (lastMessage && lastMessage.role === 'assistant') {
          lastMessage.content = `抱歉，发生错误：${error.message}`;
          lastMessage.isStreaming = false;
        } else {
          // 如果没有AI消息，则添加一个新的错误消息
          this.messages.push({
            role: 'assistant',
            content: `抱歉，发生错误：${error.message}`
          });
        }
      } finally {
        this.isLoading = false;
        // 滚动到底部
        this.scrollToBottom();
      }
    },
    
    scrollToBottom() {
      // 等待DOM更新后滚动
      this.$nextTick(() => {
        if (this.$refs.messagesContainer) {
          this.$refs.messagesContainer.scrollTop = this.$refs.messagesContainer.scrollHeight;
        }
      });
    },
    
    formatMessage(content) {
      // 简单的消息格式化，将换行符转换为<br>
      return content.replace(/\n/g, '<br>');
    },
    
    async handleFileUpload(event) {
      const file = event.target.files[0];
      if (!file) return;
      
      this.uploadingFile = true;
      this.uploadStatus = null;
      
      try {
        const formData = new FormData();
        formData.append('file', file);
        
        const response = await axios.post('/api/rag/upload', formData, {
          headers: {
            'Content-Type': 'multipart/form-data'
          }
        });
        
        if (response.data.success) {
          this.uploadStatus = {
            success: true,
            message: `上传成功: ${response.data.filename}`
          };
          // 刷新RAG状态
          this.getRagStatus();
        } else {
          throw new Error(response.data.error || '上传失败');
        }
      } catch (error) {
        this.uploadStatus = {
          success: false,
          message: `上传失败: ${error.message}`
        };
      } finally {
        this.uploadingFile = false;
        // 清空文件输入
        if (this.$refs.fileInput) {
          this.$refs.fileInput.value = '';
        }
      }
    },
    
    async addSampleDocuments() {
      if (this.isLoading) return;
      
      this.isLoading = true;
      
      try {
        const response = await axios.post('/api/rag/add_sample');
        
        if (response.data.success) {
          this.uploadStatus = {
            success: true,
            message: '示例文档添加成功'
          };
          // 刷新RAG状态
          this.getRagStatus();
        } else {
          throw new Error(response.data.error || '添加失败');
        }
      } catch (error) {
        this.uploadStatus = {
          success: false,
          message: `添加失败: ${error.message}`
        };
      } finally {
        this.isLoading = false;
      }
    }
  }
};
</script>

<style scoped>
.chat-container {
  display: flex;
  flex-direction: column;
  height: 100vh;
  max-width: 800px;
  margin: 0 auto;
  background-color: #f5f5f5;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
}

.chat-header {
  background-color: #42b983;
  color: white;
  padding: 1rem;
  text-align: center;
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

.chat-header h1 {
  margin: 0;
  font-size: 1.5rem;
}

.rag-status {
  font-size: 0.85rem;
  margin-top: 0.5rem;
  opacity: 0.9;
}

.context-indicator {
  font-size: 0.8rem;
  color: #666;
  margin-bottom: 0.3rem;
  font-style: italic;
}

.rag-controls {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  padding: 0.5rem 1rem;
  background-color: #f8f8f8;
  border-bottom: 1px solid #e0e0e0;
}

.upload-section {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.rag-controls button {
  padding: 0.5rem 1rem;
  background-color: #3498db;
  color: white;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  font-size: 0.9rem;
  transition: background-color 0.3s;
}

.rag-controls button:hover:not(:disabled) {
  background-color: #2980b9;
}

.rag-controls button:disabled {
  background-color: #bdc3c7;
  cursor: not-allowed;
}

.upload-section span {
  font-size: 0.85rem;
}

.upload-section .success {
  color: #27ae60;
}

.upload-section .error {
  color: #e74c3c;
}

.chat-messages {
  flex: 1;
  overflow-y: auto;
  padding: 1rem;
  background-color: #fff;
}

.message {
  display: flex;
  margin-bottom: 1rem;
  animation: fadeIn 0.3s ease-in-out;
}

@keyframes fadeIn {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: translateY(0); }
}

.message.user {
  justify-content: flex-end;
}

.message.assistant {
  justify-content: flex-start;
}

.message-avatar {
  min-width: 30px;
  height: 30px;
  border-radius: 50%;
  background-color: #42b983;
  color: white;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.8rem;
  margin: 0 0.5rem;
}

.message.user .message-avatar {
  background-color: #3498db;
}

.message-content {
  max-width: 70%;
  padding: 0.5rem 1rem;
  border-radius: 18px;
  background-color: #f1f1f1;
  line-height: 1.4;
  word-wrap: break-word;
}

.message.user .message-content {
  background-color: #42b983;
  color: white;
}

.chat-input {
  display: flex;
  padding: 1rem;
  background-color: #fff;
  border-top: 1px solid #e0e0e0;
}

.chat-input textarea {
  flex: 1;
  padding: 0.75rem;
  border: 1px solid #ddd;
  border-radius: 20px;
  resize: none;
  font-family: inherit;
  font-size: 1rem;
  outline: none;
  transition: border-color 0.3s;
}

.chat-input textarea:focus {
  border-color: #42b983;
}

.chat-input button {
  margin-left: 0.5rem;
  padding: 0 1.5rem;
  background-color: #42b983;
  color: white;
  border: none;
  border-radius: 20px;
  cursor: pointer;
  font-size: 1rem;
  transition: background-color 0.3s;
}

.chat-input button:hover:not(:disabled) {
  background-color: #35495e;
}

.chat-input button:disabled {
  background-color: #bdc3c7;
  cursor: not-allowed;
}

.typing-indicator {
  display: flex;
  align-items: center;
}

.typing-indicator span {
  width: 6px;
  height: 6px;
  background-color: #999;
  border-radius: 50%;
  margin: 0 2px;
  animation: typing 1.4s infinite ease-in-out;
}

.typing-indicator span:nth-child(2) {
  animation-delay: 0.2s;
}

.typing-indicator span:nth-child(3) {
  animation-delay: 0.4s;
}

@keyframes typing {
  0%, 60%, 100% {
    transform: translateY(0);
  }
  30% {
    transform: translateY(-5px);
  }
}
</style>